/*
 * Copyright (c) 2024 endless-sky
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef FOUNDATION_MUXER_INCLUDE_AAC_PCM_DECODER_H_
#define FOUNDATION_MUXER_INCLUDE_AAC_PCM_DECODER_H_

#include <cstdint>
#include <memory>
#include <vector>

#include <gst/gst.h>
#include <gst/app/gstappsrc.h>
#include <gst/app/gstappsink.h>

namespace El {
namespace Muxer {

/**
 * @brief AAC to PCM decoder.
 *
 * This class processes AAC audio data (ADTS format) incrementally and decodes it to raw PCM.
 * Feed AAC data chunks via ProcessInput and retrieve PCM data using GetData.
 * Output PCM format is 16-bit signed little-endian (S16LE).
 */
class AacPcmDecoder {
public:
    /**
     * @brief PCM sample format enumeration.
     */
    enum class PcmFormat {
        S16LE, // 16-bit signed little-endian (most common)
        S32LE, // 32-bit signed little-endian
        FLT    // 32-bit float
    };

    /**
     * @brief Constructor.
     */
    AacPcmDecoder();

    /**
     * @brief Destructor. Cleans up GStreamer resources.
     */
    ~AacPcmDecoder();

    // Disable copy and move semantics
    AacPcmDecoder(const AacPcmDecoder&) = delete;
    AacPcmDecoder& operator=(const AacPcmDecoder&) = delete;
    AacPcmDecoder(AacPcmDecoder&&) = delete;
    AacPcmDecoder& operator=(AacPcmDecoder&&) = delete;

    /**
     * @brief Initializes the decoder. Must be called before processing data.
     *
     * @param output_sample_rate Desired output PCM sample rate (e.g., 16000, 24000, 48000).
     * @param output_channels Desired number of output channels (e.g., 1 or 2).
     * @param pcm_format Desired output PCM format (S16LE, S32LE, or FLT). Default: S16LE.
     * @return true if initialization is successful, false otherwise.
     */
    bool Initialize(int output_sample_rate, int output_channels, PcmFormat pcm_format = PcmFormat::S16LE);

    /**
     * @brief Processes a chunk of input AAC data.
     *
     * The input data should be in ADTS format (AAC with headers).
     * Call GetData() after this to retrieve decoded PCM samples.
     *
     * @param data Pointer to the input AAC data.
     * @param size Size of the input data chunk in bytes.
     * @return true if processing is successful, false on error.
     */
    bool ProcessInput(const uint8_t* data, size_t size);

    /**
     * @brief Retrieves available decoded PCM data.
     *
     * This method should be called after ProcessInput() to get the decoded PCM samples.
     * When called with flush=true, it will also flush internal buffers to get remaining data.
     *
     * @param buffer Vector to store the PCM data. Will be cleared and filled with new data.
     * @param flush If true, flush internal buffers to get all remaining data (call once at end of stream).
     * @return true if successful (buffer may be empty if no data available), false on error.
     */
    bool GetData(std::vector<uint8_t>& buffer, bool flush = false);

    /**
     * @brief Gets the configured output sample rate.
     * @return Output sample rate in Hz, or 0 if not initialized.
     */
    int GetOutputSampleRate() const { return output_sample_rate_; }

    /**
     * @brief Gets the configured output channels.
     * @return Number of output channels, or 0 if not initialized.
     */
    int GetOutputChannels() const { return output_channels_; }

    /**
     * @brief Gets the configured output PCM format.
     * @return Output PCM format.
     */
    PcmFormat GetOutputFormat() const { return pcm_format_; }

private:
    // Private helper methods for decoding logic
    bool BuildPipeline(int sample_rate, int channels, PcmFormat format);
    bool PushBuffer(const uint8_t* data, size_t size);
    bool PullSamples(std::vector<uint8_t>& out, bool drain);
    const char* GetGstSampleFormat(PcmFormat format) const;

    GstElement* pipeline_ = nullptr;
    GstElement* appsrc_ = nullptr;
    GstElement* appsink_ = nullptr;

    // Configuration
    int output_sample_rate_ = 0;
    int output_channels_ = 0;
    PcmFormat pcm_format_ = PcmFormat::S16LE;
    int output_bytes_per_sample_ = 0;

    bool initialized_ = false;
    bool decoder_flushed_ = false;
    bool eos_pushed_ = false;
    int64_t bytes_pushed_ = 0;
};

} // namespace Muxer
} // namespace El

#endif // FOUNDATION_MUXER_INCLUDE_AAC_PCM_DECODER_H_
